﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace VisionLibaray
{
    public static class ImageProcessing
    {
        /// <summary>
        /// 将YV12解码为RGB
        /// </summary>
        /// <param name="pIn">YV12图像指针</param>
        /// <param name="imageOut">RGB图像,不能为空, 且尺寸与YV12相同</param>
        public static void DecodeRGB(IntPtr pIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageOut == null) throw new Exception("output image is null");

                int W = imageOut.Width;
                int H = imageOut.Height;
                long size = W * H;
                int indexY = 0, indexUV = 0;

                unsafe
                {
                    byte* yData = (byte*)pIn.ToPointer();
                    byte* vData = &yData[size];
                    byte* uData = &vData[size >> 2];

                    if (imageOut == null) imageOut = new Bitmap(W, H, PixelFormat.Format24bppRgb);

                    lock (imageOut)
                    {
                        BitmapData dataOutRGB = imageOut.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                        byte* pOutRGB = (byte*)dataOutRGB.Scan0.ToPointer();


                        byte y = 0, u = 0, v = 0;
                        double temp = 0;

                        for (int h = 0; h < H; h++)
                        {
                            for (int w = 0; w < W; w++)
                            {
                                indexY = h * W + w;
                                indexUV = h / 2 * W / 2 + w / 2;

                                y = yData[indexY];
                                v = vData[indexUV];
                                u = uData[indexUV];

                                temp = 1.164383 * (y - 16) + 2.017230 * (u - 128) + 0;
                                pOutRGB[indexY * 3 + 0] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                                temp = (1.164383 * (y - 16) - 0.391762 * (u - 128) - 0.812969 * (v - 128));
                                pOutRGB[indexY * 3 + 1] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                                temp = 1.164383 * (y - 16) + 0 + 1.596016 * (v - 128);
                                pOutRGB[indexY * 3 + 2] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                            }
                        }

                        imageOut.UnlockBits(dataOutRGB);
                    }
                }
            }
        }

        static int W, H;
        /// <summary>
        /// 将YV12解码为Grey
        /// </summary>
        /// <param name="pIn">YV12图像指针</param>
        /// <param name="imageOut">Grey图像,不能为空, 且尺寸与YV12相同</param>
        public static void DecodeGrey(IntPtr pIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageOut == null) throw new Exception("output image is null");
                if (W <= 0 || H <= 0) 
                { 
                    W = imageOut.Width;
                    H = imageOut.Height; 
                }
                long size = W * H;
                int indexY = 0;

                unsafe
                {
                    byte* yData = (byte*)pIn.ToPointer();
                    lock (imageOut)
                    {
                        BitmapData dataOutGrey = imageOut.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                        byte* pOutGrey = (byte*)dataOutGrey.Scan0.ToPointer();

                        for (int h = 0; h < H; h++)
                        {
                            for (int w = 0; w < W; w++)
                            {
                                indexY = h * W + w;

                                for (int i = 0; i < 3; i++)
                                {
                                    pOutGrey[indexY * 3 + i] = yData[indexY];
                                }

                                //pOutGrey[indexY * 3] = yData[indexY];
                            }
                        }

                        imageOut.UnlockBits(dataOutGrey);
                    }
                }
            }
        }

        /// <summary>
        /// 边缘模糊化
        /// </summary>
        /// <param name="imageIn">背景图像</param>
        /// <param name="imageOut">边缘模糊的图像</param>
        /// <param name="pCenter">边缘模糊的图像中心点在背景图像中的坐标</param>
        public static void Fuzzy(Bitmap imageIn, Bitmap imageOut, Point pCenter)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int h, v, sum = 0, WIn = imageIn.Width, xIn, yIn, WOut = imageOut.Width;
                int[] avgColor = new int[3];
                Point pointOut;

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            pointOut = new Point(pCenter.X - imageOut.Width / 2, pCenter.Y - imageOut.Height / 2);

                            for (int yOut = 0; yOut < imageOut.Height; yOut++)
                            {
                                yIn = yOut + pointOut.Y;

                                for (int xOut = 0; xOut < imageOut.Width; xOut++)
                                {
                                    xIn = xOut + pointOut.X;

                                    if (!rectIn.Contains(xIn, yIn))
                                    {
                                        for (int k = 0; k < 3; k++)
                                            pOut[(yOut * WOut + xOut) * 3 + k] = 0;

                                        continue;
                                    }

                                    #region 直接映射
                                    //for (int i = 0; i < 3; i++)
                                    //{
                                    //    pOut[(yOut * WOut + xOut) * 3 + i] = pIn[(yIn * WIn + xIn) * 3 + i];
                                    //}
                                    #endregion

                                    #region 均匀模糊化
                                    //for (int i = 0; i < 3; i++)
                                    //{
                                    //    avgColor[i] = 0;

                                    //    for (int j = -5; j <= 5; j++)
                                    //    {
                                    //        for (int k = -5; k <= 5; k++)
                                    //        {
                                    //            avgColor[i] += pIn[((yIn + j) * WIn + xIn + k) * 3 + i];
                                    //        }
                                    //    }

                                    //    pOut[(yOut * WOut + xOut) * 3 + i] = (byte)(avgColor[i] / 121);
                                    //}
                                    #endregion

                                    #region 边缘模糊化
                                    h = Math.Abs(xOut - (imageOut.Width >> 1)) / 10 + 1;
                                    v = Math.Abs(yOut - (imageOut.Height >> 1)) / 10 + 1;

                                    sum = 0;

                                    if (h == 1 && v == 1)
                                    {
#if DECODE_RGB
                                for (int i = 0; i < 3; i++)
                                {
                                    pOut[(yOut * WOut + xOut) * 3 + i] = pIn[(yIn * WIn + xIn) * 3 + i];
                                }
#else
                                        pOut[(yOut * WOut + xOut) * 3] = pIn[(yIn * WIn + xIn) * 3];
                                        pOut[(yOut * WOut + xOut) * 3 + 1] = pIn[(yIn * WIn + xIn) * 3];
                                        pOut[(yOut * WOut + xOut) * 3 + 2] = pIn[(yIn * WIn + xIn) * 3];
#endif
                                    }
                                    else
                                    {
                                        for (int k = 0; k < 3; k++) avgColor[k] = 0;

                                        for (int j = yIn - v / 2; j < yIn + v / 2 + v % 2; j++)
                                        {
                                            for (int i = xIn - h / 2; i < xIn + h / 2 + h % 2; i++)
                                            {
                                                if (!rectIn.Contains(i, j)) continue;

                                                sum++;

#if DECODE_RGB
                                        for (int k = 0; k < 3; k++)
                                        {
                                            avgColor[k] += pIn[(j * WIn + i) * 3 + k];
                                        }
#else
                                                avgColor[0] += pIn[(j * WIn + i) * 3];
#endif
                                            }
                                        }

#if DECODE_RGB
                                for (int i = 0; i < 3; i++)
                                {
                                    if (sum == 0)
                                    {
                                        pOut[(yOut * WOut + xOut) * 3 + i] = pIn[(yIn * WIn + xIn) * 3 + i];
                                    }
                                    else
                                    {
                                        pOut[(yOut * WOut + xOut) * 3 + i] = (byte)(avgColor[i] / sum);
                                    }
                                }
#else
                                        if (sum == 0)
                                        {
                                            for (int i = 0; i < 3; i++)
                                            {
                                                pOut[(yOut * WOut + xOut) * 3 + i] = pIn[(yIn * WIn + xIn) * 3 + i];
                                            }
                                        }
                                        else
                                        {
                                            pOut[(yOut * WOut + xOut) * 3] = (byte)(avgColor[0] / sum);
                                            pOut[(yOut * WOut + xOut) * 3 + 1] = pOut[(yOut * WOut + xOut) * 3];
                                            pOut[(yOut * WOut + xOut) * 3 + 2] = pOut[(yOut * WOut + xOut) * 3];

                                        }
#endif

                                    }
                                    #endregion

                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }

            }
        }

        /// <summary>
        /// 边界提取
        /// </summary>
        /// <param name="imageIn">原始图像</param>
        /// <param name="imageOut">边界图像</param>
        public static void Boundary(Bitmap imageIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WOut = imageOut.Width;

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            for (int yOut = 1; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 1; xOut < rectOut.Width - 1; xOut++)
                                {
                                    pOut[(yOut * WOut + xOut) * 3] = (byte)Math.Abs(
                                        3 * pIn[(yOut * WOut + xOut) * 3]
                                        - pIn[(yOut * WOut + xOut + 1) * 3]         //右
                                        //- pIn[(yOut * WOut + xOut - 1) * 3]         //左
                                        - pIn[((yOut + 1) * WOut + xOut) * 3]       //下
                                        //- pIn[((yOut - 1) * WOut + xOut) * 3]       //上
                                        //- pIn[((yOut - 1) * WOut + xOut + 1) * 3]   //右上
                                        //- pIn[((yOut - 1) * WOut + xOut - 1) * 3]   //左上
                                        - pIn[((yOut + 1) * WOut + xOut + 1) * 3]   //右下
                                        //- pIn[((yOut + 1) * WOut + xOut - 1) * 3]   //左下
                                        );
                                    //pOut[(yOut * WOut + xOut) * 3] = (byte)((
                                    //    4 * pIn[(yOut * WOut + xOut) * 3]
                                    //    - pIn[((yOut - 1) * WOut + xOut) * 3]
                                    //    - pIn[((yOut + 1) * WOut + xOut) * 3]
                                    //    - pIn[(yOut * WOut + xOut + 1) * 3]
                                    //    - pIn[(yOut * WOut + xOut - 1) * 3]
                                    //    ) / 4);
                                    pOut[(yOut * WOut + xOut) * 3 + 1] = pOut[(yOut * WOut + xOut) * 3];
                                    pOut[(yOut * WOut + xOut) * 3 + 2] = pOut[(yOut * WOut + xOut) * 3];
                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

        /// <summary>
        /// 兴奋灰度增强
        /// </summary>
        /// <param name="imageIn">原始图像</param>
        /// <param name="imageOut">兴趣灰度增强图像</param>
        public static void GreyEnhance(Bitmap imageIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WOut = imageOut.Width;

                int[] GreyHistogram = new int[256];

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            //灰度直方图
                            for (int yOut = 1; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 1; xOut < rectOut.Width - 1; xOut++)
                                {
                                    GreyHistogram[pIn[(yOut * WOut + xOut) * 3]]++;
                                }
                            }

                            byte maxGrey = 0;
                            for (int i = 1; i < 256; i++)
                            {
                                if (GreyHistogram[i] > GreyHistogram[maxGrey]) maxGrey = (byte)i;
                            }

                            for (int yOut = 1; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 1; xOut < rectOut.Width - 1; xOut++)
                                {
                                    if (Math.Abs(maxGrey - pIn[(yOut * WOut + xOut) * 3]) < 20)
                                    {
                                        pOut[(yOut * WOut + xOut) * 3] =
                                        pOut[(yOut * WOut + xOut) * 3 + 1] =
                                        pOut[(yOut * WOut + xOut) * 3 + 2] =
                                        pIn[(yOut * WOut + xOut) * 3];
                                    }
                                    else
                                    {
                                        pOut[(yOut * WOut + xOut) * 3] =
                                        pOut[(yOut * WOut + xOut) * 3 + 1] =
                                        pOut[(yOut * WOut + xOut) * 3 + 2] = 0;
                                    }
                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

        /// <summary>
        /// 提取块的灰度均值作为抽象块的灰度
        /// 相当于将原始图片的块映射到抽象图的每一个像素点上
        /// </summary>
        /// <param name="imageIn">原始图片</param>
        /// <param name="imageOut">抽象图片</param>
        /// <param name="sizeBlock">块的大小</param>
        /// <param name="sizeMove">相邻块左边框之间距离</param>
        public static void Abstract(Bitmap imageIn, Bitmap imageOut, Size sizeBlock, Size sizeMove)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WIn = imageIn.Width, WOut = imageOut.Width;

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            int xIn, yIn, sum;
                            for (int yOut = 0; yOut < rectOut.Height; yOut++)
                            {
                                yIn = yOut * sizeMove.Height;

                                for (int xOut = 0; xOut < rectOut.Width; xOut++)
                                {
                                    xIn = xOut * sizeMove.Width;

                                    sum = 0;

                                    for (int yBlock = 0; yBlock < sizeBlock.Height; yBlock++)
                                    {
                                        if (yIn + yBlock >= imageIn.Height) continue;

                                        for (int xBlock = 0; xBlock < sizeBlock.Width; xBlock++)
                                        {
                                            if (xIn + xBlock >= imageIn.Width) continue;

                                            sum += pIn[((yIn + yBlock) * WIn + xIn + xBlock) * 3];
                                        }
                                    }

                                    pOut[(yOut * WOut + xOut) * 3] =
                                    pOut[(yOut * WOut + xOut) * 3 + 1] =
                                    pOut[(yOut * WOut + xOut) * 3 + 2] =
                                    (byte)(sum / (sizeBlock.Width * sizeBlock.Height));

                                    //pOut[(yOut * WOut + xOut) * 3] =
                                    //    pOut[(yOut * WOut + xOut) * 3 + 1] =
                                    //    pOut[(yOut * WOut + xOut) * 3 + 2] =
                                    //    pIn[(yIn * WIn + xIn) * 3];
                                }

                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

        /// <summary>
        /// 视频各像素点直方图
        /// </summary>
        //public static Dictionary<byte, int>[,] VideoPixelHistogram;
        /// <summary>
        /// 稳定背景
        ///     存储方图进中计数最大的灰度值
        /// </summary>
        public static byte[,] StableBackground;
        public static void StableStream(Bitmap imageIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WIn = imageIn.Width, WOut = imageOut.Width;

                //if (VideoPixelHistogram == null)
                //     VideoPixelHistogram = new Dictionary<byte, int>[imageOut.Width, imageOut.Height];

                if (StableBackground == null)
                    StableBackground = new byte[imageOut.Width, imageOut.Height];
                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());


                            for (int yOut = 0; yOut < rectOut.Height; yOut++)
                            {

                                for (int xOut = 0; xOut < rectOut.Width; xOut++)
                                {
                                    byte key = pIn[(yOut * WOut + xOut) * 3];

                                    byte maxKey = StableBackground[xOut, yOut];

                                    //if (VideoPixelHistogram[xOut, yOut] == null)
                                    //    VideoPixelHistogram[xOut, yOut] = new Dictionary<byte, int>();

                                    //if (!VideoPixelHistogram[xOut, yOut].ContainsKey(key))
                                    //    VideoPixelHistogram[xOut, yOut].Add(key, 0);

                                    //VideoPixelHistogram[xOut, yOut][key]++;

                                    //if (!VideoPixelHistogram[xOut, yOut].ContainsKey(maxKey)
                                    //    ||maxKey != key &&
                                    //    VideoPixelHistogram[xOut, yOut][key] > VideoPixelHistogram[xOut, yOut][maxKey])
                                    if (maxKey == 0) StableBackground[xOut, yOut] = key;
                                    else if (key != maxKey) StableBackground[xOut, yOut] += (byte)((key - maxKey + 9) / 10);
                                    //else if (key > maxKey) StableBackground[xOut, yOut]++;
                                    //else if (key < maxKey) StableBackground[xOut, yOut]--;
                                    pOut[(yOut * WOut + xOut) * 3] =
                                    pOut[(yOut * WOut + xOut) * 3 + 1] =
                                    pOut[(yOut * WOut + xOut) * 3 + 2] =
                                    StableBackground[xOut, yOut];
                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

        public static byte[,] EliminateNoise;
        /// <summary>
        /// 提取与背景做差值，提取动态像素
        /// </summary>
        /// <param name="imageIn"></param>
        /// <param name="imageOut"></param>
        public static void Subtract(Bitmap imageIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WIn = imageIn.Width, WOut = imageOut.Width;

                if (EliminateNoise == null) EliminateNoise = new byte[imageOut.Width, imageOut.Height];

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            for (int yOut = 0; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 0; xOut < rectOut.Width - 1; xOut++)
                                {
                                    EliminateNoise[xOut, yOut] =
                                    (byte)(Math.Abs(StableBackground[xOut, yOut]
                                    - pIn[(yOut * WOut + xOut) * 3]) > 50 ? 255 : 0);
                                }
                            }

                            for (int yOut = 1; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 1; xOut < rectOut.Width - 1; xOut++)
                                {
                                    if (EliminateNoise[xOut, yOut] > 0
                                        && EliminateNoise[xOut + 1, yOut] > 0
                                        //&& EliminateNoise[xOut - 1, yOut] > 0
                                        && EliminateNoise[xOut, yOut + 1] > 0
                                        //&& EliminateNoise[xOut, yOut - 1] > 0
                                        )
                                    {
                                        pOut[(yOut * WOut + xOut) * 3] =
                                        pOut[(yOut * WOut + xOut) * 3 + 1] =
                                        pOut[(yOut * WOut + xOut) * 3 + 2] = 255;
                                    }
                                    else
                                        pOut[(yOut * WOut + xOut) * 3] =
                                        pOut[(yOut * WOut + xOut) * 3 + 1] =
                                        pOut[(yOut * WOut + xOut) * 3 + 2] = 0;

                                    EliminateNoise[xOut, yOut] = 0;
                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

        static Font font = new Font("宋体", 20);
        static PointF pointXY = new PointF(0, 0);

        public static void EliminateShake(Bitmap imageIn, Bitmap imageOut)
        {
            lock (typeof(ImageProcessing))
            {
                if (imageIn == null || imageOut == null) throw new Exception("image is null");

                int WIn = imageIn.Width, WOut = imageOut.Width;

                try
                {
                    unsafe
                    {
                        lock (imageOut)
                        {
                            Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                            Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);

                            BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                            byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                            byte* pOut = (byte*)(dataOut.Scan0.ToPointer());

                            int minActiveCount = 0;
                            byte subtract = 0;
                            for (int yOut = 1; yOut < rectOut.Height - 1; yOut++)
                            {
                                for (int xOut = 1; xOut < rectOut.Width - 1; xOut++)
                                {
                                    subtract = (byte)(Math.Abs(StableBackground[xOut, yOut]
                                        - pIn[(yOut * WOut + xOut) * 3]) > 50 ? 255 : 0);
                                    if (subtract > 0) minActiveCount++;
                                }
                            }

                            if (minActiveCount == 0)
                            {
                                imageOut.UnlockBits(dataOut);
                                imageIn.UnlockBits(dataIn);
                                return;
                            }

                            int activeCount = 0, xDelt = 0, yDelt = 0;
                            do
                            {
                                xDelt++;
                                if (activeCount > 0
                                    && activeCount < minActiveCount) minActiveCount = activeCount;

                                activeCount = 0;

                                for (int yOut = Math.Abs(yDelt); yOut < rectOut.Height - Math.Abs(yDelt); yOut++)
                                {
                                    for (int xOut = Math.Abs(xDelt); xOut < rectOut.Width - Math.Abs(xDelt); xOut++)
                                    {
                                        subtract = (byte)(Math.Abs(StableBackground[xOut, yOut]
                                            - pIn[((yOut + yDelt) * WOut + xOut + xDelt) * 3]) > 50 ? 255 : 0);
                                        if (subtract > 0) activeCount++;
                                    }
                                }
                            } while (activeCount < minActiveCount);

                            xDelt--;

                            if (xDelt == 0)
                            {
                                do
                                {
                                    xDelt--;
                                    if (activeCount > 0
                                        && activeCount < minActiveCount) minActiveCount = activeCount;

                                    activeCount = 0;

                                    for (int yOut = Math.Abs(yDelt); yOut < rectOut.Height - Math.Abs(yDelt); yOut++)
                                    {
                                        for (int xOut = Math.Abs(xDelt); xOut < rectOut.Width - Math.Abs(xDelt); xOut++)
                                        {
                                            subtract = (byte)(Math.Abs(StableBackground[xOut, yOut]
                                                - pIn[((yOut + yDelt) * WOut + xOut + xDelt) * 3]) > 50 ? 255 : 0);
                                            if (subtract > 0) activeCount++;
                                        }
                                    }
                                } while (activeCount < minActiveCount);
                            }

                            xDelt++;

                            do
                            {
                                yDelt++;
                                if (activeCount > 0
                                    && activeCount < minActiveCount) minActiveCount = activeCount;

                                activeCount = 0;

                                for (int yOut = Math.Abs(yDelt); yOut < rectOut.Height - Math.Abs(yDelt); yOut++)
                                {
                                    for (int xOut = Math.Abs(xDelt); xOut < rectOut.Width - Math.Abs(xDelt); xOut++)
                                    {
                                        subtract = (byte)(Math.Abs(StableBackground[xOut, yOut]
                                            - pIn[((yOut + yDelt) * WOut + xOut + xDelt) * 3]) > 50 ? 255 : 0);
                                        if (subtract > 0) activeCount++;
                                    }
                                }
                            } while (activeCount < minActiveCount);

                            yDelt--;

                            if (yDelt == 0)
                            {
                                do
                                {
                                    yDelt--;
                                    if (activeCount > 0
                                        && activeCount < minActiveCount) minActiveCount = activeCount;

                                    activeCount = 0;

                                    for (int yOut = Math.Abs(yDelt); yOut < rectOut.Height - Math.Abs(yDelt); yOut++)
                                    {
                                        for (int xOut = Math.Abs(xDelt); xOut < rectOut.Width - Math.Abs(xDelt); xOut++)
                                        {
                                            subtract = (byte)(Math.Abs(StableBackground[xOut, yOut]
                                                - pIn[((yOut + yDelt) * WOut + xOut + xDelt) * 3]) > 50 ? 255 : 0);
                                            if (subtract > 0) activeCount++;
                                        }
                                    }
                                } while (activeCount < minActiveCount);
                            }

                            yDelt++;

                            for (int yOut = Math.Abs(yDelt); yOut < rectOut.Height - Math.Abs(yDelt); yOut++)
                            {
                                for (int xOut = Math.Abs(xDelt); xOut < rectOut.Width - Math.Abs(xDelt); xOut++)
                                {
                                    pOut[(yOut * WOut + xOut) * 3] =
                                    pOut[(yOut * WOut + xOut) * 3 + 1] =
                                    pOut[(yOut * WOut + xOut) * 3 + 2] =
                                    (byte)(Math.Abs(StableBackground[xOut, yOut]
                                    - pIn[((yOut + yDelt) * WOut + xOut + xDelt) * 3]) > 50 ? 255 : 0);
                                }
                            }

                            imageOut.UnlockBits(dataOut);
                            imageIn.UnlockBits(dataIn);

                            Graphics G = Graphics.FromImage(imageOut);
                            G.DrawString(string.Format("x = {0}, y = {1}", xDelt, yDelt), font, Brushes.Lime, pointXY);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    throw Ex;
                }
            }
        }

    }
}
